package cfca.seal.web.service;

import cfca.com.itextpdf.text.pdf.PdfReader;
import cfca.sadk.algorithm.common.PKIException;
import cfca.sadk.org.bouncycastle.util.encoders.DecoderException;
import cfca.sadk.util.CertUtil;
import cfca.sadk.util.KeyUtil;
import cfca.sadk.x509.certificate.X509CRL;
import cfca.sadk.x509.certificate.X509Cert;
import cfca.seal.bean.AdminUser;
import cfca.seal.bean.Organization;
import cfca.seal.bean.Seal;
import cfca.seal.bean.SealCert;
import cfca.seal.bean.SealImage;
import cfca.seal.bean.SealUser;
import cfca.seal.common.Page;
import cfca.seal.dao.main.IAdminUserDao;
import cfca.seal.dao.main.IOrganizationDao;
import cfca.seal.dao.main.ISealCertDao;
import cfca.seal.dao.main.ISealDao;
import cfca.seal.dao.main.ISealImageDao;
import cfca.seal.exception.CodeException;
import cfca.seal.factory.DonePdfSealFactory;
import cfca.seal.factory.PrePdfSealFactory;
import cfca.seal.front.service.ws.util.ValidityUtil;
import cfca.seal.maker.util.SealExtracter;
import cfca.seal.maker.util.SealMaker;
import cfca.seal.mananger.CrlManager;
import cfca.seal.mananger.PdfSealManager;
import cfca.seal.mananger.SealManager;
import cfca.seal.sadk.DonePdfSeal;
import cfca.seal.sadk.PrePdfReader;
import cfca.seal.sadk.PrePdfSeal;
import cfca.seal.system.Env;
import cfca.seal.util.EncryptUtil;
import cfca.seal.util.SealCertUtil;
import cfca.seal.util.StringUtil;
import cfca.seal.util.TimeUtil;
import cfca.seal.util.VerifyUtil;
import cfca.seal.web.vo.PdfSealVo;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Resource;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class SealService
{
  private static final Logger log = LoggerFactory.getLogger("web_sys");

  @Resource
  ISealDao dao;

  @Resource
  ISealCertDao certdao;

  @Resource
  ISealImageDao modeldao;

  @Resource
  IAdminUserDao managerdao;

  @Resource
  ISealCertDao sealCertDao;

  @Resource
  IOrganizationDao orgdao;

  public Seal getSealById(String id) throws Exception { Seal seal = this.dao.getSealById(id);
    return seal; }

  public SealCert getSealCertById(String id) throws Exception
  {
    SealCert sealCert = this.certdao.getSealCertById(id);
    return sealCert;
  }

  public Page getSeals(Seal seal, String pageNo, AdminUser manager) throws Exception {
    Page page = this.dao.getPage(seal, pageNo, manager.getUserType() > 0 ? manager.getOrganizationId() : "");
    return page;
  }

  public boolean save(Seal seal) throws Exception {
    try {
      Seal existSeal = this.dao.getSealByCode(seal.getCode());
      if (existSeal != null) {
        throw new CodeException("C1008", "重复的印章编码");
      }

      if (-1 != seal.getCertType()) {
        CheckIsValidateCert(seal);
      }
      SealImage sealImage = this.modeldao.getSealModelById(seal.getSealImageId());
      seal.setType(sealImage.getType());
      seal.setSealImageData(sealImage.getSealImageData());
      seal.setImageHeight(sealImage.getImageHeight());
      seal.setImageWidth(sealImage.getImageWidth());

      if ((seal.getSealPfxData() != null) && (seal.getSealPfxData().length > 0)) {
        AdminUser manager = this.managerdao.getManagerById(seal.getCreator());
        Organization org = this.orgdao.getOrganizationById(seal.getOrganizationId());
        SealMaker maker = new SealMaker(seal.getSealPfxData(), seal.getSealPfxPwd(), seal.getSealImageData(), manager.getRealName(), org.getName(), seal.getName());

        seal.setSealData(maker.createSeal());
      }
      seal.setSealPfxPwd(EncryptUtil.encrypto(seal.getSealPfxPwd()));
      seal.setSealPwd(EncryptUtil.encrypto(seal.getSealPwd()));

      if (("sqlite".equals(Env.databaseType)) && (-1 != seal.getCertType())) {
        PdfSealManager.getInstance().addPdfSeal(seal);
        SealManager.getInstance().addSeal(seal);
      }
      return this.dao.insert(seal);
    } catch (CodeException ce) {
      log.error(ce.getMessage());
      throw ce;
    } catch (PKIException pkie) {
      log.error(pkie.getMessage());
      if ("851001".equals(pkie.getErrCode())) {
        throw new CodeException("C8005", "证书密码不正确", pkie);
      }
      throw new CodeException("C0001", "制作印章发生异常", pkie);
    }
    catch (DecoderException de) {
      log.error(de.getMessage());
      throw new CodeException("C8004", "证书内容不正确", de);
    } catch (Exception e) {
      log.error(e.getMessage());
      throw new CodeException("C0001", "制作印章发生异常", e);
    }
  }

  public boolean updatestatus(String id, String status) throws Exception {
    if ("sqlite".equals(Env.databaseType)) {
      SealManager.getInstance().updateStatus(id, status);
    }
    return this.dao.updateStatus(id, status);
  }

  private void CheckIsValidateCert(Seal sealBean) throws Exception {
    X509Cert x509Cert = null;
    if (0 == sealBean.getCertType()) {
      if ((null == sealBean.getSealPfxData()) || (sealBean.getSealPfxData().length == 0))
        throw new CodeException("C3009", "证书不能为空");
      try
      {
        if (SealCertUtil.isSM2(sealBean.getSealPfxData())) {
          x509Cert = CertUtil.getCertFromSM2(sealBean.getSealPfxData());
          KeyUtil.getPrivateKeyFromSM2(sealBean.getSealPfxData(), sealBean.getSealPfxPwd());
        } else {
          x509Cert = CertUtil.getCertFromPFX(sealBean.getSealPfxData(), sealBean.getSealPfxPwd());
          KeyUtil.getPrivateKeyFromPFX(sealBean.getSealPfxData(), sealBean.getSealPfxPwd());
        }
      } catch (PKIException e) {
        if (e.getMessage().contains("verifyMac faulture")) {
          throw new CodeException("C8005", "证书密码不正确");
        }
        if ("850605".equals(e.getErrCode())) {
          throw new CodeException("C8004", "证书内容不正确");
        }
        throw new CodeException(e.getErrCode(), e.getErrDesc());
      }
    } else if ((2 == sealBean.getCertType()) || (1 == sealBean.getCertType())) {
      SealCert sealCert = this.sealCertDao.getSealCertById(sealBean.getSealCertId());
      x509Cert = new X509Cert(sealCert.getPublickCertData());
    }

    if (!VerifyUtil.verifyCertDate(x509Cert, TimeUtil.getCurrentTime())) {
      throw new CodeException("C9001", "证书已过期或者还未生效");
    }

    X509CRL x509crl = CrlManager.getX509CRL(StringUtil.formatIssuerKey(x509Cert.getIssuer()));
    if ((x509crl != null) && (VerifyUtil.verifyByCRL(x509Cert, x509crl)))
      throw new CodeException("C9002", "证书已经吊销");
  }

  public void extractFirstPageImage(InputStream input, String outputPath)
    throws IOException
  {
    PDDocument doc = PDDocument.load(input);

    List pages = doc.getDocumentCatalog().getAllPages();
    PDPage page = (PDPage)pages.get(0);
    BufferedImage image = page.convertToImage();
    Iterator iter = ImageIO.getImageWritersBySuffix("png");
    ImageWriter writer = (ImageWriter)iter.next();
    File outFile = new File(outputPath);
    FileOutputStream out = new FileOutputStream(outFile);
    ImageOutputStream outImage = ImageIO.createImageOutputStream(out);
    writer.setOutput(outImage);
    writer.write(new IIOImage(image, null, null));
    doc.close();
  }

  public boolean bindUser(String sealId, String bindid) throws Exception {
    this.dao.removeBind(sealId);
    if (StringUtil.isEmpty(bindid))
      return true;
    String[] bindids = bindid.split(",");
    List list = new ArrayList();
    Date createTime = new Date();
    if (bindids.length > 0) {
      for (String userId : bindids) {
        SealUser sealUser = new SealUser(StringUtil.generateID(), sealId, userId, createTime);
        list.add(sealUser);
      }
      return this.dao.bindUser(list);
    }
    return false;
  }

  public String getBindUser(String id) throws Exception {
    List<String> bindUserIds = this.dao.getBindUserId(id);
    String result = "";
    if ((bindUserIds != null) && (bindUserIds.size() > 0)) {
      for (String userid : bindUserIds) {
        if (StringUtil.isNotEmpty(result))
          result = result + ",";
        result = result + userid;
      }
    }
    return result;
  }

  public byte[] doSealForPdf(byte[] pdfData, Seal seal, PdfSealVo pdfSealVo) throws CodeException {
    byte[] result = "".getBytes();
    try {
      PrePdfSeal prePdfSeal = null;
      if (seal.getCertType() == 0) {
        SealExtracter sEx = new SealExtracter(seal.getSealData(), EncryptUtil.decrypto(seal.getSealPfxPwd()));
        int keyType = sEx.getKeyType();
        if (1 == keyType)
          prePdfSeal = PrePdfSealFactory.generatePrePdfSeal(seal);
        else if (2 == keyType)
          prePdfSeal = PrePdfSealFactory.generatePrePdfSealSm2(seal);
      }
      else if ((seal.getCertType() == 1) || (seal.getCertType() == 2)) {
        SealCert cert = this.certdao.getSealCertById(seal.getSealCertId());
        prePdfSeal = PrePdfSealFactory.generatePrePdfSeal(seal, cert);
      } else {
        throw new CodeException("C0001", "没有对应的证书文件");
      }
      DonePdfSeal donePdfSeal = DonePdfSealFactory.generateDonePdfSeal(prePdfSeal, pdfData, "服务器PDF签章", "服务器页面");
      float offsetX = StringUtil.isEmpty(pdfSealVo.getOffsetX()) ? 0.0F : Float.parseFloat(pdfSealVo.getOffsetX());
      float offsetY = StringUtil.isEmpty(pdfSealVo.getOffsetY()) ? 0.0F : Float.parseFloat(pdfSealVo.getOffsetY());
      if ("1".equals(pdfSealVo.getSealType()))
      {
        donePdfSeal.updateLocationInfoByBlankSignature(pdfSealVo.getBlankFieldName());
        result = donePdfSeal.createPdfSeal(Env.timeStampServerUrl, Env.timeStampServerUser, Env.timeStampServerPasswd, pdfSealVo.getCertificationLevel());
      }
      else if ("2".equals(pdfSealVo.getSealType()))
      {
        PdfReader reader = new PdfReader(pdfData);

        List pages = new ArrayList();
        String sealPage = pdfSealVo.getSealPage();
        if (sealPage.contains(",")) {
          for (String p : sealPage.split(","))
            pages.add(p);
        }
        else if (sealPage.contains("-")) {
          for (int j = Integer.valueOf(sealPage.split("-")[0]).intValue(); j <= Integer.valueOf(sealPage.split("-")[1]).intValue(); j++)
            pages.add(String.valueOf(j));
        }
        else if ("0".equals(sealPage)) {
          for (int i = 1; i <= reader.getNumberOfPages(); i++)
            pages.add(String.valueOf(i));
        }
        else {
          pages.add(sealPage);
        }

        byte[] resultTemp = "".getBytes("UTF-8");
        for (int k = 0; k < pages.size(); k++) {
          String page = (String)pages.get(k);
          ValidityUtil.checkPageXY(page, pdfSealVo.getLx(), pdfSealVo.getLy(), reader.getNumberOfPages());
          int lx = Integer.parseInt(pdfSealVo.getLx());
          int ly = Integer.parseInt(pdfSealVo.getLy());
          int rx = lx + seal.getImageWidth();
          int ry = ly + seal.getImageHeight();
          donePdfSeal.updateLocationInfoByCoordinate(Integer.parseInt(page), lx, ly, rx, ry);
          donePdfSeal.offsetCoordLocationInfo(offsetX, offsetY);
          resultTemp = donePdfSeal.createPdfSeal(Env.timeStampServerUrl, Env.timeStampServerUser, Env.timeStampServerPasswd, pdfSealVo.getCertificationLevel());

          donePdfSeal.initPdfReader(new PrePdfReader(resultTemp));
        }
        result = resultTemp;
      } else if ("3".equals(pdfSealVo.getSealType()))
      {
        donePdfSeal.updateLocationInfoByKeyword(pdfSealVo.getSealKeyword());
        if ("U".equals(pdfSealVo.getLocationStyle()))
          donePdfSeal.offsetCoordLocationInfo(2);
        else if ("D".equals(pdfSealVo.getLocationStyle()))
          donePdfSeal.offsetCoordLocationInfo(3);
        else if ("L".equals(pdfSealVo.getLocationStyle()))
          donePdfSeal.offsetCoordLocationInfo(4);
        else if ("R".equals(pdfSealVo.getLocationStyle())) {
          donePdfSeal.offsetCoordLocationInfo(5);
        }
        donePdfSeal.offsetCoordLocationInfo(offsetX, offsetY);
        result = donePdfSeal.createPdfSeal(Env.timeStampServerUrl, Env.timeStampServerUser, Env.timeStampServerPasswd, pdfSealVo.getCertificationLevel());
      }
    }
    catch (CodeException ce) {
      log.error(ce.getMessage(), ce);
      throw ce;
    } catch (Exception e) {
      log.error("PDF盖章发生异常", e);
      if (null != e.getMessage()) {
        if (e.getMessage().contains("not found!"))
          throw new CodeException("C0001", "关键字：" + pdfSealVo.getSealKeyword() + "没有找到", e);
        if (e.getMessage().contains("No blank signature found in the pdf file data!")) {
          throw new CodeException("C0001", "该pdf文档没有标签域", e);
        }
        throw new CodeException("C0001", "PDF盖章发生异常", e);
      }

      throw new CodeException("C0001", "PDF盖章发生异常", e);
    }

    return result;
  }
}